﻿using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using NuGet.ProjectModel;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using Quartz.Impl.Triggers;
using Quartz.NET.Web.Constant;
using Quartz.NET.Web.Enum;
using Quartz.NET.Web.Models;
using Quartz.NET.Web.Utility;
using Quartz.Spi;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Quartz.NET.Web.Extensions
{
    public static class QuartzNETExtension
    {
        private static List<TaskOptions> _taskList = new List<TaskOptions>();

        private static IFreeSql freeSql => HttpContext.Current.RequestServices.GetService<IFreeSql>();

        /// <summary>
        /// 初始化作业
        /// </summary>
        /// <param name="applicationBuilder"></param>
        /// <param name="env"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseQuartz(this IApplicationBuilder applicationBuilder, IWebHostEnvironment env)
        {
            IServiceProvider services = applicationBuilder.ApplicationServices;

            ISchedulerFactory _schedulerFactory = services.GetService<ISchedulerFactory>();


            IFreeSql fsql = services.GetService<IFreeSql>();

            int errorCount = 0;
            string errorMsg = "";
            TaskOptions options = null;
            try
            {
                _taskList = fsql.Select<TaskOptions>().ToList();// JsonConvert.DeserializeObject<List<TaskOptions>>(jobConfig);
                _taskList.ForEach(x =>
                {
                    options = x;
                    var result = x.AddJob(_schedulerFactory, true, jobFactory: services.GetService<IJobFactory>()).GetAwaiter().GetResult();
                });
            }
            catch (Exception ex)
            {
                errorCount = +1;
                errorMsg += $"作业:{options?.taskName},异常：{ex.Message}";
            }
            string content = $"成功:{_taskList.Count - errorCount}个,失败{errorCount}个,异常：{errorMsg}\r\n";

            return applicationBuilder;
        }

        /// <summary>
        /// 获取所有的作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <returns></returns>
        public static async Task<List<TaskOptions>> GetJobs(this ISchedulerFactory schedulerFactory)
        {
            List<TaskOptions> list = new List<TaskOptions>();

            try
            {
                list = await freeSql.Select<TaskOptions>().OrderBy(a=>a.Create_Date).ToListAsync();

                IScheduler _scheduler = await schedulerFactory.GetScheduler();
                for (int i = 0; i < list.Count; i++)
                {
                    var t = list[i];
                    var jobState = _scheduler.GetTriggerState(new TriggerKey(t.taskName, t.groupName));

                    switch (jobState.Result)
                    {
                        case TriggerState.Normal:
                            t.status = 0;
                            break;
                        case TriggerState.Paused:
                            t.status = 1;
                            break;
                        case TriggerState.None:
                            t.status = 5;
                            break;
                        case TriggerState.Error:
                            t.status = 3;
                            break;
                        case TriggerState.Blocked:
                            t.status = 4;
                            break;
                        case TriggerState.Complete:
                            t.status = 2;
                            break;
                        default:
                            break;
                    }

                }
                /*
                            var groups = await _scheduler.GetJobGroupNames();

                            var joblist=await _scheduler.GetPausedTriggerGroups();


                            foreach (var groupName in groups)
                            {
                                var groupJobs = _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName)).Result.ToList();

                                //  foreach (var jobKey in await _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName)))
                                for (int i = 0; i < groupJobs.Count; i++)
                                {
                                    var jobKey = groupJobs[i];
                                    TaskOptions taskOptions = _taskList.Where(x => x.groupName == jobKey.Group && x.taskName == jobKey.Name)
                                        .FirstOrDefault();
                                    if (taskOptions == null)
                                        continue;

                                    var triggers = await _scheduler.GetTriggersOfJob(jobKey);
                                    foreach (ITrigger trigger in triggers)
                                    {
                                        DateTimeOffset? dateTimeOffset = trigger.GetPreviousFireTimeUtc();
                                        if (dateTimeOffset != null)
                                        {
                                            taskOptions.lastRunTime = Convert.ToDateTime(dateTimeOffset.ToString());
                                        }
                                        else
                                        {
                                            var runlog = FileQuartz.GetJobRunLog(taskOptions.Id, 1, 2);
                                            if (runlog.Count > 0)
                                            {
                                                DateTime.TryParse(runlog[0].BeginDate, out DateTime lastRunTime);
                                                taskOptions.lastRunTime = lastRunTime;
                                            }
                                        }
                                    }
                                    list.Add(taskOptions);
                                }
                            }
                            */
            }
            catch (Exception ex)
            {
                Log.Error("获取作业异常：" + ex.Message + ex.StackTrace);

            }
            return list;
        }

        /// <summary>
        /// 添加作业
        /// </summary>
        /// <param name="taskOptions"></param>
        /// <param name="schedulerFactory"></param>
        /// <param name="init">是否初始化,否=需要重新生成配置文件，是=不重新生成配置文件</param>
        /// <returns></returns>
        public static async Task<object> AddJob(this TaskOptions taskOptions, ISchedulerFactory schedulerFactory, bool init = false, IJobFactory jobFactory = null)
        {
            try
            {
                if (!string.IsNullOrWhiteSpace(taskOptions.interval))
                {
                    (bool, string) validExpression = taskOptions.interval.IsValidExpression();
                    if (!validExpression.Item1)
                    {
                        return new { status = false, msg = validExpression.Item2 };
                    }

                }
                else if (taskOptions.startTime != null)
                {
                    if (DateTime.Now > taskOptions.startTime)
                    {
                        taskOptions.status = (int)TriggerState.None;
                    }
                }
                else
                {
                    taskOptions.status = (int)TriggerState.None;
                }

                (bool, object) result = taskOptions.Exists(init);
                if (!result.Item1)
                {
                    return result.Item2;
                }

                if (!init)
                {
                    taskOptions.Id = Guid.NewGuid().ToString();
                    taskOptions.Create_Date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                    taskOptions.Modify_Date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                    int r = freeSql.Insert(taskOptions).ExecuteAffrows();
                    _taskList.Add(taskOptions);
                }

                IJobDetail job = JobBuilder.Create<HttpResultfulJob>()
                    .WithIdentity(taskOptions.taskName, taskOptions.groupName)
                    .Build();
                ITrigger trigger = null;
                if (!string.IsNullOrWhiteSpace(taskOptions.interval))
                {
                    trigger = TriggerBuilder.Create()
                     .WithIdentity(taskOptions.taskName, taskOptions.groupName)
                     .StartNow()
                     .WithDescription(taskOptions.describe)
                     .WithCronSchedule(taskOptions.interval)
                     .Build();
                }
                else
                {
                    DateTimeOffset df = (DateTimeOffset)taskOptions.startTime;
                    trigger = TriggerBuilder.Create()
                     .WithIdentity(taskOptions.taskName, taskOptions.groupName)
                     .StartAt(df)
                     .WithDescription(taskOptions.describe)
                     //   .WithCronSchedule(taskOptions.interval)
                     .Build();
                }



                IScheduler scheduler = await schedulerFactory.GetScheduler();

                if (jobFactory == null)
                {
                    try
                    {
                        jobFactory = HttpContext.Current.RequestServices.GetService<IJobFactory>();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"创建任务[{taskOptions.taskName}]异常,{ex.Message}");
                    }
                }

                if (jobFactory != null)
                {
                    scheduler.JobFactory = jobFactory;
                }
                if (taskOptions.status != (int)TriggerState.None)
                {
                    await scheduler.ScheduleJob(job, trigger);
                    await scheduler.Start();

                    if (taskOptions.status != (int)TriggerState.Normal)
                    {
                        await scheduler.PauseTrigger(trigger.Key);
                    }
                    else
                    {
                        Log.Information($"作业:{taskOptions.taskName},分组:{taskOptions.groupName},新建时未启动原因,状态为:{taskOptions.status}");

                    }
                    if (!init)
                    {
                        FileQuartz.WriteJobAction(JobAction.新增, taskOptions.taskName, taskOptions.groupName);
                    }

                }


            }
            catch (Exception ex)
            {
                return new { status = false, msg = ex.Message };
            }
            return new { status = true };
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="taskOptions"></param>
        /// <param name="schedulerFactory">job fact</param>
        /// <returns></returns>
        public static async Task<object> UpdateJob(this TaskOptions taskOptions, ISchedulerFactory schedulerFactory)
        {
            try
            {
                IJobFactory jobFactory = HttpContext.Current.RequestServices.GetService<IJobFactory>();
                ITrigger trigger = null;
                if (!string.IsNullOrWhiteSpace(taskOptions.interval))
                {
                    (bool, string) validExpression = taskOptions.interval.IsValidExpression();
                    if (!validExpression.Item1)
                    {
                        return new { status = false, msg = validExpression.Item2 };
                    }
                    trigger = TriggerBuilder.Create()
                        .WithIdentity(taskOptions.taskName, taskOptions.groupName)
                        .StartNow()
                        .WithDescription(taskOptions.describe)
                        .WithCronSchedule(taskOptions.interval)
                        .Build();
                }
                else
                {

                    if (taskOptions.startTime == null)
                    {
                        return new { status = false, msg = "启动时间不可为空！" };
                    }
                    DateTimeOffset df = (DateTimeOffset)taskOptions.startTime;
                    trigger = TriggerBuilder.Create()
                         .WithIdentity(taskOptions.taskName, taskOptions.groupName)
                         .StartAt(df)
                         .WithDescription(taskOptions.describe)
                         //   .WithCronSchedule(taskOptions.interval)
                         .Build();
                }

                // int r = freeSql.Update<TaskOptions>()
                // .Set(a => a.startTime == taskOptions.startTime)
                // .Set(a => a.interval == taskOptions.interval)
                // .Set(a => a.Modify_Date == DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
                // .Where(a => a.groupName == taskOptions.groupName && a.taskName == taskOptions.taskName)
                // .ExecuteAffrows();
                taskOptions.Modify_Date=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                int r=freeSql.Update<TaskOptions>().SetSource(taskOptions).ExecuteAffrows();

                _taskList = freeSql.Select<TaskOptions>().ToList();

                IJobDetail job = JobBuilder.Create<HttpResultfulJob>()
                    .WithIdentity(taskOptions.taskName, taskOptions.groupName)
                    .Build();
                IScheduler scheduler = await schedulerFactory.GetScheduler();

                scheduler.JobFactory = jobFactory;

                await scheduler.ScheduleJob(job, trigger);
                if (taskOptions.status == (int)TriggerState.Normal)
                {
                    await scheduler.Start();
                }
                else
                {
                    await schedulerFactory.Pause(taskOptions);
                    Log.Information($"作业:{taskOptions.taskName},分组:{taskOptions.groupName},新建时未启动原因,状态为:{taskOptions.status}");
                }

            }
            catch (Exception ex)
            {
                return new { status = false, msg = ex.Message };
            }
            return new { status = true };
        }

        /// <summary>
        /// 移除作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskOptions"></param>
        /// <returns></returns>
        public static Task<object> Remove(this ISchedulerFactory schedulerFactory, TaskOptions taskOptions)
        {
            return schedulerFactory.TriggerAction(taskOptions.taskName, taskOptions.groupName, JobAction.删除, taskOptions);
        }

        /// <summary>
        /// 批量移除作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskOptions"></param>
        /// <returns></returns>
        public static async Task BatchRemove(this ISchedulerFactory schedulerFactory, List<TaskOptions> taskList)
        {
            for (int i = 0; i < taskList.Count; i++)
            {
                var taskOptions = taskList[i];
               await schedulerFactory.TriggerAction(taskOptions.taskName, taskOptions.groupName, JobAction.删除, taskOptions);
            }
       
        }

        /// <summary>
        /// 更新作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskOptions"></param>
        /// <returns></returns>
        public static Task<object> Update(this ISchedulerFactory schedulerFactory, TaskOptions taskOptions)
        {
            return schedulerFactory.TriggerAction(taskOptions.taskName, taskOptions.groupName, JobAction.修改, taskOptions);
        }

        /// <summary>
        /// 暂停作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskOptions"></param>
        /// <returns></returns>
        public static Task<object> Pause(this ISchedulerFactory schedulerFactory, TaskOptions taskOptions)
        {
            return schedulerFactory.TriggerAction(taskOptions.taskName, taskOptions.groupName, JobAction.暂停, taskOptions);
        }

        /// <summary>
        /// 启动作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskOptions"></param>
        /// <returns></returns>
        public static Task<object> Start(this ISchedulerFactory schedulerFactory, TaskOptions taskOptions)
        {
            return schedulerFactory.TriggerAction(taskOptions.taskName, taskOptions.groupName, JobAction.开启, taskOptions);
        }

        /// <summary>
        /// 立即执行一次作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskOptions"></param>
        /// <returns></returns>
        public static Task<object> Run(this ISchedulerFactory schedulerFactory, TaskOptions taskOptions)
        {
            return schedulerFactory.TriggerAction(taskOptions.taskName, taskOptions.groupName, JobAction.立即执行, taskOptions);
        }

        /// <summary>
        /// 修改数据
        /// </summary>
        /// <param name="taskOptions"></param>
        /// <param name="schedulerFactory"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static object ModifyTaskEntity(this TaskOptions taskOptions, ISchedulerFactory schedulerFactory, JobAction action)
        {
            TaskOptions options = null;
            int r = 0;
            object result = null;
            switch (action)
            {
                case JobAction.删除:
                    r = freeSql.Delete<TaskOptions>(taskOptions).ExecuteAffrows();
                    _taskList = freeSql.Select<TaskOptions>().ToList();
                    break;
                case JobAction.修改:
                    ////生成任务并添加新配置
                    result = taskOptions.UpdateJob(schedulerFactory).GetAwaiter().GetResult();
                    break;
                case JobAction.暂停:
                case JobAction.开启:
                case JobAction.停止:
                case JobAction.立即执行:
                    options = _taskList.Where(x => x.taskName == taskOptions.taskName && x.groupName == taskOptions.groupName).FirstOrDefault();
                    if (action == JobAction.暂停)
                    {
                        options.status = (int)TriggerState.Paused;
                    }
                    else if (action == JobAction.停止)
                    {
                        options.status = (int)action;
                    }
                    else
                    {
                        options.status = (int)TriggerState.Normal;
                    }
                    taskOptions.status = options.status;
                    taskOptions.Modify_Date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                    r = freeSql.Update<TaskOptions>().SetSource(taskOptions).ExecuteAffrows();
                    break;
            }
            //生成配置文件
            //   FileQuartz.WriteJobConfig(_taskList);
            // FileQuartz.WriteJobAction(action, taskOptions.taskName, taskOptions.groupName, "操作对象：" + JsonConvert.SerializeObject(taskOptions));
            return result;
        }

        /// <summary>
        /// 触发新增、删除、修改、暂停、启用、立即执行事件
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskName"></param>
        /// <param name="groupName"></param>
        /// <param name="action"></param>
        /// <param name="taskOptions"></param>
        /// <returns></returns>
        public static async Task<object> TriggerAction(this ISchedulerFactory schedulerFactory, string taskName, string groupName, JobAction action, TaskOptions taskOptions = null)
        {
            string errorMsg = "";
            try
            {
                IScheduler scheduler = await schedulerFactory.GetScheduler();

                List<JobKey> jobKeys = scheduler.GetJobKeys(GroupMatcher<JobKey>.AnyGroup()).Result.ToList();
                JobKey jobKey = null;
                jobKey = jobKeys.Where(s => scheduler.GetTriggersOfJob(s)
             .Result.Any(x => x.JobKey.Name == taskName)).FirstOrDefault();
                ITrigger trigger = await scheduler.GetTrigger(new TriggerKey(taskName, groupName));
                if (trigger == null)
                {
                    // errorMsg = $"未找到触发器[{taskName}]";
                    // return new { status = false, msg = errorMsg };
                }
                object result = null;
                switch (action)
                {
                    case JobAction.修改:
                    case JobAction.删除:
                        if (trigger != null)
                        {
                            await scheduler.PauseTrigger(trigger.Key);
                            await scheduler.UnscheduleJob(trigger.Key);// 移除触发器
                            await scheduler.DeleteJob(trigger.JobKey);
                        }

                        result = taskOptions.ModifyTaskEntity(schedulerFactory, action);
                        break;
                    case JobAction.暂停:
                    case JobAction.停止:
                    case JobAction.开启:

                        if (trigger != null)
                        {
                            if (action == JobAction.暂停)
                            {
                                await scheduler.PauseTrigger(trigger.Key);
                            }
                            else if (action == JobAction.开启)
                            {
                                await scheduler.ResumeTrigger(trigger.Key);
                            }
                            else
                            {
                                await scheduler.Shutdown();
                            }
                            result = taskOptions.ModifyTaskEntity(schedulerFactory, action);
                        }
                        else
                        {
                            errorMsg = $"未找到触发器[{taskName}]";
                             return new { status = false, msg = errorMsg };
                        }

                        break;
                    case JobAction.立即执行:
                        await scheduler.TriggerJob(jobKey);
                        break;
                }
                return result ?? new { status = true, msg = $"作业{action.ToString()}成功" };
            }
            catch (Exception ex)
            {
                errorMsg = ex.Message;
                return new { status = false, msg = ex.Message };
            }
            finally
            {
                FileQuartz.WriteJobAction(action, taskName, groupName, errorMsg);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>通过作业上下文获取作业对应的配置参数
        /// <returns></returns>
        public static TaskOptions GetTaskOptions(this IJobExecutionContext context)
        {
            AbstractTrigger trigger = (context as JobExecutionContextImpl).Trigger as AbstractTrigger;
            TaskOptions taskOptions = _taskList.Where(x => x.taskName == trigger.Name && x.groupName == trigger.Group).FirstOrDefault();
            return taskOptions ?? _taskList.Where(x => x.taskName == trigger.JobName && x.groupName == trigger.JobGroup).FirstOrDefault();
        }

        /// <summary>
        /// 作业是否存在
        /// </summary>
        /// <param name="taskOptions"></param>
        /// <param name="init">初始化的不需要判断</param>
        /// <returns></returns>
        public static (bool, object) Exists(this TaskOptions taskOptions, bool init)
        {
            if (!init && _taskList.Any(x => x.taskName == taskOptions.taskName && x.groupName == taskOptions.groupName))
            {
                return (false,
                    new
                    {
                        status = false,
                        msg = $"作业:{taskOptions.taskName},分组：{taskOptions.groupName}已经存在"
                    });
            }
            return (true, null);
        }

        public static (bool, string) IsValidExpression(this string cronExpression)
        {
            try
            {
                CronTriggerImpl trigger = new CronTriggerImpl();
                trigger.CronExpressionString = cronExpression;
                DateTimeOffset? date = trigger.ComputeFirstFireTimeUtc(null);
                return (date != null, date == null ? $"请确认表达式{cronExpression}是否正确!" : "");
            }
            catch (Exception e)
            {
                return (false, $"请确认表达式{cronExpression}是否正确!{e.Message}");
            }
        }
    }

}
